在開始今天的實作前,我想先帶各位了解JWT(JSON Web Token)是啥,以及它的應用
JWT是一種用於client與server間共享安全資訊的開放標準
每一組JWT token包含了header(演算法&token的種類), payload(資料)與verify signature(確保資訊在傳輸時沒有改變)
首先需安裝jsonwebtoken
當使用者註冊或登入系統時,我們要透過cookie回傳一組jwt token給他們
所以也需安裝cookie-parser
由於jwt token是在使用者註冊或登入才被創造出來的,所以要用mongoose method去操作user(document)
UserSchema.methods.getSignedJwtToken = function() {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE
});
}
JWT_SECRET或JWT_EXPIRE都可以自己設定
完成jwt token的設定後,我們來想想該怎麼將token回傳給user
const sendTokenResponse = (user, statusCode, res) => {
// Create token
const token = user.getSignedJwtToken();
const options = {
expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
httpOnly: true
}
res
.status(statusCode)
.cookie('token', token, options)
.json({
success: true,
token
});
}
回傳給使用者的cookie須包含:名稱、token與options
此處的options設置了cookie的到期日為30天&禁止javascript直接存取cookie(httpOnly)
由於多了回傳jwt token這項功能,我們要將昨天register controller的回傳值改為
sendTokenResponse(user, 200, res);
在登入的部分,就變得有一點點複雜
我們要處理的情況有:
// 第一種情況
// 先destructure object
const { email, password } = req.body;
if (!email || !password) {
return next(new ErrorResponse('Please provide an email and password', 400));
}
// 第二種情況
在寫user的schema時,為了避免搜尋結果return password,password的select為false
但我們等等在驗證第三種情況時,需要驗證輸入的密碼是否正確,所以在此處要回傳password field
const user = await User.findOne({ email }).select('+password');
if (!user) {
return next(new ErrorResponse('Invalid credentials', 401));
}
// 第三種情況
const isMatch = await user.matchPassword(password);
if (!isMatch) {
return next(new ErrorResponse('Invalid credentials', 401));
}
還記得使用者在註冊時,我們有將他們輸入的密碼用bcryptjs加密後,才存入db對吧
但要如何將加密後的密碼與user login輸入的密碼作比較呢?
可以用bcrypt.compare來比較!
一樣用mongoose method去操作user(document)
UserSchema.methods.matchPassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
}